﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace B121B {
    /// <summary>
    /// The different types of fuel
    /// </summary>
    public enum Fuel { Diesel, Octan92, Octan95 };
    /// <summary>
    /// The different licenses a driver can have
    /// </summary>
    public enum DriversLicense { A, B, C, D, BE, CE, DE };
    /// <summary>
    /// The different vehicle types
    /// </summary>
    public enum VehicleTypes { PersonCar, Truck, Van, Bus, AutoCamper };

    /// <summary>
    /// The vehicle base class
    /// </summary>
    public abstract class Vehicle {
        public Vehicle() { } // One way to instantiate a vehicle

        /// <summary>
        /// Vehicle constructor
        /// </summary>
        /// <param name="brand">Brand</param>
        /// <param name="model">Model</param>
        /// <param name="km">Distance driven</param>
        /// <param name="year">Production year</param>
        /// <param name="price">Price</param>
        /// <param name="motorSize">Size of motor</param>
        /// <param name="km_l">Travel distance on 1 liter of fuel</param>
        /// <param name="fueltype">Fueltype</param>
        public Vehicle(string brand, string model, int km, DateTime year, double price, double motorSize, double km_l, Fuel fueltype) { // Another way to instantiate a vehicle
            Brand = brand;
            Model = model;
            KM = km;
            SetYear(year);
            Price = price;
            MotorSize = motorSize;
            KM_L = km_l;
            FuelType = fueltype;
        }

        #region Properties
        /// <summary>
        /// The vehicle type
        /// </summary>
        public virtual VehicleTypes VehicleType { get; set; }

        /// <summary>
        /// The brand of the vehicle
        /// </summary>
        public virtual string Brand {
            get { return _Brand; }
            set {
                if (value == null) { // Check for null
                    throw new ArgumentNullException("A string may not be null");
                } else if (value == "") { // Check for empty string
                    throw new ArgumentException("A brand$ cannot be empty");
                } else {
                    _Brand = value;
                }
            }
        }
        private string _Brand;

        /// <summary>
        /// The model of the vehicle
        /// </summary>
        public virtual string Model {
            get { return _Model; }
            set {
                if (value == null) { // Check for null
                    throw new ArgumentNullException("A string may not be null");
                } else if (value == "") { // Check for empty string
                    throw new ArgumentException("A brand cannot be empty");
                } else {
                    _Model = value;
                }
            }
        }
        private string _Model;

        /// <summary>
        /// The distance the vehicle has driven
        /// </summary>
        public virtual int KM {
            get { return _KM; }
            set {
                if (value < 0) { // Check for proper distance
                    throw new ArgumentOutOfRangeException("A Vehicle can not have driven less than 0 km!");
                }
                _KM = value;
            }
        }
        private int _KM;

        /// <summary>
        /// The year the vehicle was produced
        /// </summary>
        public virtual DateTime Year { get { return _Year; } }
        private DateTime _Year; // Readonly!

        private void SetYear(DateTime year) {
            if (year == null) { // Null check
                throw new ArgumentNullException("Year cannot be null");
            } else if (year.Year < 1950) { // Too early production
                _Year = new DateTime(1950, 1, 1);
            } else if (year.Year > DateTime.Now.Year) { // Not yet produced
                _Year = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
            } else {
                _Year = year;
            }
        }

        /// <summary>
        /// The price of the vehicle
        /// </summary>
        public virtual double Price {
            get { return _Price; }
            set {
                if (value <= 0) { // A vehicle cannot be free
                    throw new ArgumentOutOfRangeException("A vehicle cannot be free");
                } else {
                    _Price = value;
                }
            }
        }
        private double _Price;

        /// <summary>
        /// Max speed of the vehicle
        /// </summary>
        public abstract int MaxSpeed { get; }

        /// <summary>
        /// Motorsize of the vehicle
        /// </summary>
        public abstract double MotorSize { get; set; }

        /// <summary>
        /// How far the vehicle can drive on one liter of fuel
        /// </summary>
        public virtual double KM_L { get; set; } // Not negative?

        /// <summary>
        /// The type of fuel the vehicle uses
        /// </summary>
        public virtual Fuel FuelType { get; protected set; } // Readonly!

        /// <summary>
        /// The vehicles energy class
        /// </summary>
        public virtual char EnergyClass {
            get { return CalcEnergyClass(FuelType, KM_L); }
        }

        /// <summary>
        /// The title of the vehicle
        /// </summary>
        public virtual string Title { get { return Brand + " " + Model + " " + MotorSize; } }

        /// <summary>
        /// The seller of the vehicle
        /// </summary>
        public virtual Seller Seller {
            get { return _Seller; }
            set {
                if (value == null) {
                    throw new ArgumentNullException();
                } else {
                    _Seller = value;
                }
            }
        } // ændre til seller 
        private Seller _Seller;
        #endregion

        /// <summary>
        /// Shows the vehicle information, formatted for console output
        /// </summary>
        /// <returns>Vehicle information</returns>
        public override string ToString() {
            return
                "Title: " + Title + Environment.NewLine +
                "  Type: " + VehicleType.ToString() + Environment.NewLine +
                "  Brand: " + Brand + Environment.NewLine +
                "  Model: " + Model + Environment.NewLine +
                "  Distance: " + KM + " km" + Environment.NewLine +
                "  Year: " + Year.Year + Environment.NewLine +
                "  Price: " + Price + " DKK" + Environment.NewLine +
                "  MaxSpeed: " + MaxSpeed + " km/h" + Environment.NewLine +
                "  MotorSize: " + MotorSize + " CC" + Environment.NewLine +
                "  km/L: " + KM_L + " km/L" + Environment.NewLine +
                "  FuelType: " + FuelType + " " + Environment.NewLine +
                "  EnergyClass: " + EnergyClass + " " + Environment.NewLine +
                "  Seller: " + Seller.Name;
        }

        #region Override Equals
        public override bool Equals(object obj) {
            if (obj == null) { return false; } // Check for null

            // We don't check for reference equality

            if (this.GetType() != obj.GetType()) { return false; } // Same type?

            return Equals((Vehicle)obj); // More specific check
        }
        public bool Equals(Vehicle obj) {
            if (obj == null) { return false; }

            return // Check all values
                this.VehicleType == obj.VehicleType &&
                this.Brand == obj.Brand &&
                this.Model == obj.Model &&
                this.KM == obj.KM &&
                this.Year == obj.Year &&
                this.Price == obj.Price &&
                this.MaxSpeed == obj.MaxSpeed &&
                this.MotorSize == obj.MotorSize &&
                this.KM_L == obj.KM_L &&
                this.FuelType == obj.FuelType &&
                this.EnergyClass == obj.EnergyClass &&
                this.Seller == obj.Seller;
        }
        public override int GetHashCode() {
            return base.GetHashCode(); // We do not check has code, as we check on values only
        }

        public static bool operator ==(Vehicle obj1, Vehicle obj2) {
            if (ReferenceEquals(obj1, null)) {
                return ReferenceEquals(obj2, null); // Both null, equal, otherwise not
            }
            return obj1.Equals(obj2);
        }
        public static bool operator !=(Vehicle obj1, Vehicle obj2) {
            return !(obj1 == obj2);
        }
        #endregion

        /// <summary>
        /// Calculates the energyclass of a vehicle
        /// </summary>
        /// <param name="fuel">Fueltype the vehicle uses</param>
        /// <param name="km_l">How far the vehicle drives on one liter of fuel</param>
        /// <returns></returns>
        public static char CalcEnergyClass(Fuel fuel, double km_l) {
            char retChar = 'x'; // Default value
            switch (fuel) { // Test which fuel type
                case Fuel.Diesel:
                    if (km_l > 25) {
                        retChar = 'A';
                    } else if (km_l <= 25 && km_l > 20) {
                        retChar = 'B';
                    } else if (km_l <= 20 && km_l > 15) {
                        retChar = 'C';
                    } else if (km_l <= 15) {
                        retChar = 'D';
                    }
                    break;

                case Fuel.Octan92:
                case Fuel.Octan95:
                    if (km_l > 20) {
                        retChar = 'A';
                    } else if (km_l <= 20 && km_l > 15) {
                        retChar = 'B';
                    } else if (km_l <= 15 && km_l > 10) {
                        retChar = 'C';
                    } else if (km_l <= 10) {
                        retChar = 'D';
                    }
                    break;
            }

            return retChar;
        }

        /// <summary>
        /// Returns true if the motor size is within the limits
        /// </summary>
        /// <param name="min">Minimum (inclusive) value</param>
        /// <param name="max">Maximum (inclusive) value</param>
        /// <param name="val">Value to check</param>
        /// <returns>True if value is within limits, otherwise false</returns>
        public static bool IsMotorSizeOkay(double min, double max, double val) {
            return (val <= max && val >= min ? true : false);
        }
    }
}
